﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Web;

namespace Birkelund.QueryStrings.Internals
{
    /// <summary>
    /// Class containing common supporting functions
    /// </summary>
    internal static class Common
    {
        /// <summary>
        /// Returns current URL. Throws NullReferenceException if called outside web context
        /// </summary>
        internal static Uri CurrentUrl
        {
            get { return HttpContext.Current.Request.Url; }
        }

        /// <summary>
        /// This method converts a <see cref="NameValueCollection"/> to an IDictionary
        /// </summary>
        /// <param name="nvc"></param>
        /// <returns></returns>
        internal static IEnumerable<TokenKeyValue> ToTokenKeyValues(this NameValueCollection nvc)
        {
            if (nvc == null)
            {
                return Enumerable.Empty<TokenKeyValue>();
            }

            var q = from item in nvc.Cast<string>()
                    select new TokenKeyValue(item, nvc[item]);

            return q;
        }

        internal static IList<TokenKeyValue> GetCollectionWithRemovedKeys(this IEnumerable<TokenKeyValue> items, params ITokenKey[] keys)
        {
            List<TokenKeyValue> helperList = items.ToList();
            foreach (ITokenKey key in keys)
            {
                helperList.RemoveAll(x => Common.AreEqual(x.Key, key.KeyName));
            }

            return helperList;
        }

        /// <summary>
        /// Verifies if the strings are equal.
        /// </summary>
        /// <param name="a">First string</param>
        /// <param name="b">Second string</param>
        /// <param name="isCaseSensitive">isCaseSensitive</param>
        /// <returns>Returns true is equal. Otherwise false</returns>
        internal static bool AreEqual(string a, string b, bool isCaseSensitive)
        {
            StringComparer comparer = isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;

            return comparer.Compare(a, b) == 0;
        }

        /// <summary>
        /// Verifies if the strings are equal.
        /// </summary>
        /// <param name="a">First string</param>
        /// <param name="b">Second string</param>
        /// <returns>Returns true is equal. Otherwise false</returns>
        internal static bool AreEqual(string a, string b)
        {
            return AreEqual(a, b, false);
        }

        /// <summary>
        /// Casts the specified string value.
        /// </summary>
        /// <typeparam name="TReturn">The type of return.</typeparam>
        /// <param name="stringValue">The string value to cast.</param>
        /// <returns></returns>
        internal static TReturn Cast<TReturn>(string stringValue) where TReturn : struct
        {
            if (typeof(TReturn).IsEnum)
            {
                TReturn value = (TReturn)Enum.Parse(typeof(TReturn), stringValue, true);
                bool isValueDefined = Enum.IsDefined(typeof(TReturn), value);
                bool isFlagEnum = typeof(TReturn).GetCustomAttributes(typeof(FlagsAttribute), true).Length > 0;
                if (isValueDefined || isFlagEnum)
                {
                    return value;
                }
                else
                {
                    throw new InvalidEnumArgumentException(string.Format("Value {0} is not a valid member of the enum {1}", stringValue, typeof(TReturn).Name));
                }
            }
            else if (typeof(TReturn) == typeof(Guid))
            {
                return (TReturn)(object)new Guid(stringValue);
            }
            else if (typeof(TReturn) == typeof(TimeSpan))
            {
                return (TReturn)(object)TimeSpan.Parse(stringValue); //don't handle exceptions here
            }
            else
            {
                TReturn returnValue = (TReturn)Convert.ChangeType(stringValue, typeof(TReturn)); //just give it a shot and let it throw an exception if not possible
                return returnValue;
            }
        }

        /// <summary>
        /// Determines whether the value type is valid for ValueTypeToken.
        /// </summary>
        /// <param name="type">The candidate type.</param>
        /// <returns>
        /// 	<c>true</c> if valid; otherwise, <c>false</c>.
        /// </returns>
        internal static bool IsValidValueTypeToken(Type type)
        {
            //throw exception if type is null
            bool isCorrect =
                type.IsPrimitive ||
                type.IsEnum ||
                type == typeof(Guid) ||
                type == typeof(DateTime) ||
                type == typeof(TimeSpan);

            return isCorrect; //type.IsValueType will incorrectly allow eg. structs
        }
    }
}